package com.atguigu.gmall.wms.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.bean.PageParamVo;
import com.atguigu.gmall.common.bean.PageResultVo;
import com.atguigu.gmall.common.exception.OrderException;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.atguigu.gmall.wms.mapper.WareSkuMapper;
import com.atguigu.gmall.wms.service.WareSkuService;
import com.atguigu.gmall.wms.vo.SkuLockVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.stream.Collectors;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuMapper, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuMapper wareSkuMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    private static final String LOCK_PREFIX = "stock:lock:";
    private static final String KEY_PREFIX = "stock:info:";

    @Override
    public PageResultVo queryPage(PageParamVo paramVo) {
        IPage<WareSkuEntity> page = this.page(
                paramVo.getPage(),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageResultVo(page);
    }

    @Transactional
    @Override
    public List<SkuLockVo> checkLock(List<SkuLockVo> lockVos, String orderToken) {

        // 判空
        if (CollectionUtils.isEmpty(lockVos)) {
            throw new OrderException("要购买的商品不能为空");
        }

        // 遍历所有商品，验库存并锁库存
        lockVos.forEach(skuLockVo -> {
            checkAndLock(skuLockVo);
        });

        // 判断是否有锁定失败的记录，如果有，则解锁所有锁定成功的库存
        if (lockVos.stream().anyMatch(skuLockVo -> !skuLockVo.getLock())) {
            // 获取锁定成功的库存，并解锁
            lockVos.stream().filter(SkuLockVo::getLock).collect(Collectors.toList()).forEach(skuLockVo -> {
                wareSkuMapper.unlock(skuLockVo.getWareSkuId(),skuLockVo.getCount());
            });
            // 返回锁定信息：那些锁定失败，那些锁定成功
            return lockVos;
        }

        // 都锁定成功，为了方便将来减库存或者解锁库存，要把锁定信息放入redis
        redisTemplate.opsForValue().set(KEY_PREFIX + orderToken, JSON.toJSONString(lockVos));
        // 锁定成功之后，再返回之前，发送延时消息定时解锁库存
        this.rabbitTemplate.convertAndSend("ORDER_EXCHANGE", "stock.ttl", orderToken);
        // 都锁定成功，返回null
        return null;
    }

    private void checkAndLock(SkuLockVo skuLockVo) {
        RLock lock = redissonClient.getLock(LOCK_PREFIX + skuLockVo);
        lock.lock();

        try {
            // 先查库存，如果库存不足，锁定失败
            List<WareSkuEntity> wareSkuEntities = wareSkuMapper.check(skuLockVo.getSkuId(),skuLockVo.getCount());
            if (CollectionUtils.isEmpty(wareSkuEntities)) {
                return;
            }
            // 如果库存充足，更新库存表，从最近仓库发货（锁定），这里取第一个仓库
            WareSkuEntity wareSkuEntity = wareSkuEntities.get(0);
            if (wareSkuMapper.lock(wareSkuEntity.getId(),skuLockVo.getCount()) == 1) {
                skuLockVo.setLock(true);
                // 一旦锁定成功，需要记录锁定库存的id
                skuLockVo.setWareSkuId(wareSkuEntity.getId());
            }
        } finally {
            lock.unlock();
        }


    }

}